package com.gzx.plugin.tjzy.modular.allocate.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.json.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import com.gzx.plugin.tjzy.modular.allocate.consts.GzxAllocateType;
import com.gzx.plugin.tjzy.modular.allocate.entity.GzxAllocateRecord;
import com.gzx.plugin.tjzy.modular.allocate.entity.bo.GzxAllocateRecordAddOrEditBo;
import com.gzx.plugin.tjzy.modular.allocate.entity.bo.GzxAllocateRecordQueryBo;
import com.gzx.plugin.tjzy.modular.allocate.entity.vo.GzxAllocateCaseExcelVo;
import com.gzx.plugin.tjzy.modular.allocate.entity.vo.GzxAllocateRecordVo;
import com.gzx.plugin.tjzy.modular.allocate.service.GzxAllocateRecordDbService;
import com.gzx.plugin.tjzy.modular.allocate.service.GzxAllocateRecordService;
import com.gzx.plugin.tjzy.modular.caffeine.UserCaffeine;
import com.gzx.plugin.tjzy.modular.gzxcase.dto.GzxAllocateCaseDto;
import com.gzx.plugin.tjzy.modular.gzxcase.entity.GzxCase;
import com.gzx.plugin.tjzy.modular.gzxcase.service.GzxCaseDbService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;
import vip.xiaonuo.auth.core.pojo.SaBaseLoginUser;
import vip.xiaonuo.auth.core.util.StpLoginUserUtil;
import vip.xiaonuo.common.consts.IntConstant;
import vip.xiaonuo.common.exception.CommonException;
import vip.xiaonuo.common.tenant.TenantContext;
import vip.xiaonuo.common.util.CommonCryptogramUtil;
import vip.xiaonuo.common.util.CommonExcelUtil;
import vip.xiaonuo.common.util.CommonSleepUtil;
import vip.xiaonuo.common.util.CommonWechatUtil;
import vip.xiaonuo.common.wechat.enums.WechatNotificationType;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author zjt
 * @description
 * @date 2024/12/5 10:32
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class GzxAllocateRecordServiceImpl implements GzxAllocateRecordService {

    private final GzxAllocateRecordDbService allocateRecordDbService;

    private final GzxCaseDbService caseDbService;

    //用户缓存
    private final UserCaffeine userCaffeine;


    /**
     * 分页查询分案记录
     *
     * @param allocateRecordQueryBo 查询条件
     * @return
     */
    @Override
    public Page<GzxAllocateRecordVo> pageList(GzxAllocateRecordQueryBo allocateRecordQueryBo) {
        return allocateRecordDbService.pageList(new Page<>(allocateRecordQueryBo.getCurrent(), allocateRecordQueryBo.getSize()), Wrappers.<GzxAllocateRecord>lambdaQuery()
                .eq(GzxAllocateRecord::getBatchNumber, allocateRecordQueryBo.getBatchNumber())
                .eq(GzxAllocateRecord::getDebtorCardId, CommonCryptogramUtil.doSm1AesEncrypt(allocateRecordQueryBo.getDebtorCardId()))
                .orderByDesc(GzxAllocateRecord::getId));
    }

    /**
     * 案件回收、分配
     *
     * @param allocateRecordAddOrEditBo
     */
    @Override
    public void updateCaseMediator(GzxAllocateRecordAddOrEditBo allocateRecordAddOrEditBo) {
        List<Long> caseIdList = allocateRecordAddOrEditBo.getCaseIdList();
        List<GzxCase> caseList = null;
        //案件分配
        SaBaseLoginUser loginUser = StpLoginUserUtil.getLoginUser();
        if (StringUtils.isNotBlank(allocateRecordAddOrEditBo.getUserName())) {
            caseList = caseDbService.selectCaseList(Wrappers.<GzxCase>lambdaQuery()
                    .in(GzxCase::getId, caseIdList)
                    .eq(GzxCase::getCaseStatus, 1));
            if (caseIdList.size() != caseList.size()) {
                throw new CommonException("选择的数据包含不支持分案的状态，请先回收案件");
            }
            CommonWechatUtil.sendMessage("案件分配【开始】", "已开始执行" + loginUser.getName() + "的分案任务：共计" + caseIdList.size() + "条", WechatNotificationType.TASK_START, List.of(loginUser.getEmpNo()));
            List<GzxAllocateCaseExcelVo> allocateCaseExcelVoList = new ArrayList<>();
            for (GzxCase gzxCase : caseList) {
                GzxAllocateCaseExcelVo allocateCaseExcelVo = new GzxAllocateCaseExcelVo();
                allocateCaseExcelVo.setBatchNumber(gzxCase.getBatchNumber());
                allocateCaseExcelVo.setDebtorCardId(gzxCase.getDebtorCardId());
                allocateCaseExcelVo.setMediatorName(allocateRecordAddOrEditBo.getUserName());
                allocateCaseExcelVoList.add(allocateCaseExcelVo);
            }
            allocateProcess(allocateCaseExcelVoList, loginUser);
        } else {
            //案件回收
            caseList = caseDbService.selectCaseList(Wrappers.<GzxCase>lambdaQuery()
                    .in(GzxCase::getId, caseIdList)
                    .eq(GzxCase::getCaseStatus, 2));
            if (CollectionUtil.isNotEmpty(caseList)) {
                String userId = loginUser.getId();
                List<GzxAllocateRecord> withdrawResult = new ArrayList<>();
                for (GzxCase gzxCase : caseList) {
                    GzxAllocateRecord allocateRecord = new GzxAllocateRecord();
                    allocateRecord.setType(GzxAllocateType.WITHDRAW);
                    allocateRecord.setBatchNumber(gzxCase.getBatchNumber());
                    allocateRecord.setDebtorCardId(gzxCase.getDebtorCardId());
                    allocateRecord.setMediatorId(null);
                    allocateRecord.setCreateUser(userId);
                    allocateRecord.setTenantId(StpLoginUserUtil.getLoginUser().getTenantId());
                    withdrawResult.add(allocateRecord);
                }
                allocateRecordDbService.saveBatch(withdrawResult);
                caseDbService.update(Wrappers.<GzxCase>lambdaUpdate()
                        .set(GzxCase::getCaseStatus, 1)
                        .set(GzxCase::getMediatorId, null)
                        .set(GzxCase::getMediatorName, null)
                        .set(GzxCase::getAffiliatedCompany, null)
                        .in(GzxCase::getId, caseIdList));
            }
        }
    }

    /**
     * Excel分案
     *
     * @param file
     */
    @Override
    public void excelAllocate(MultipartFile file) {
        SaBaseLoginUser loginUser = StpLoginUserUtil.getLoginUser();
        List<GzxAllocateCaseExcelVo> allocateCaseExcelVoList = CommonExcelUtil.importExcel(file, GzxAllocateCaseExcelVo.class);
        CommonWechatUtil.sendMessage("案件分配【开始】", "已开始执行" + loginUser.getName() + "的分案任务：共计" + allocateCaseExcelVoList.size() + "条", WechatNotificationType.TASK_START, List.of(loginUser.getEmpNo()));
        List<GzxAllocateCaseExcelVo> newList = allocateCaseExcelVoList.stream().filter(s -> StringUtils.isNotBlank(s.getMediatorName())).toList();
        int cha = allocateCaseExcelVoList.size() - newList.size();
        if (cha > 0) {
            CommonWechatUtil.sendMessage("案件分配【异常】", WechatNotificationType.TASK_START, List.of(loginUser.getEmpNo()), loginUser.getName() + "的分案任务", cha + "条因调解员为空，未完成分案，其余正常分案");
        }
        allocateProcess(newList, loginUser);
        allocateRecordDbService.modifyCaseOverdueAmount(newList);
    }

    private void allocateProcess(List<GzxAllocateCaseExcelVo> allocateCaseExcelVoList, SaBaseLoginUser loginUser) {
        Map<String, Map<String, List<String>>> caseMap = buildCaseMap(allocateCaseExcelVoList);
        List<GzxAllocateRecord> allocateRecordList = buildAllocateRecord(caseMap);
        Thread.startVirtualThread(() -> saveAllocateToDb(caseMap, allocateRecordList, loginUser));
    }

    private Map<String, Map<String, List<String>>> buildCaseMap(List<GzxAllocateCaseExcelVo> allocateCaseExcelVoList) {
        Map<String, Map<String, List<String>>> map = new HashMap<>();
        for (GzxAllocateCaseExcelVo caseExcelVo : allocateCaseExcelVoList) {
            Map<String, List<String>> map1 = map.get(caseExcelVo.getBatchNumber());
            if (CollectionUtils.isEmpty(map1)) {
                map1 = new HashMap<>();
            }
            List<String> debtorCardIdList = map1.get(caseExcelVo.getMediatorName());
            if (CollectionUtils.isEmpty(debtorCardIdList)) {
                debtorCardIdList = new ArrayList<>();
            }
            debtorCardIdList.add(CommonCryptogramUtil.doSm1AesEncrypt(caseExcelVo.getDebtorCardId()));
            map1.put(caseExcelVo.getMediatorName(), debtorCardIdList);
            map.put(caseExcelVo.getBatchNumber(), map1);
        }
        return map;
    }

    private List<GzxAllocateRecord> buildAllocateRecord(Map<String, Map<String, List<String>>> caseMap) {
        Map<String, JSONObject> userMap = userCaffeine.getUserMap();
        List<GzxAllocateRecord> allocateRecordList = new ArrayList<>();
        String tenantId = StpLoginUserUtil.getLoginUser().getTenantId();
        for (Map.Entry<String, Map<String, List<String>>> entry : caseMap.entrySet()) {
            String batchNumber = entry.getKey();
            //根据调解员分组
            for (Map.Entry<String, List<String>> entry2 : entry.getValue().entrySet()) {
                String mediatorName = entry2.getKey();
                JSONObject userVo = userMap.get(mediatorName);
                if (userVo != null) {
                    allocateRecordList.addAll(getBatchAllocateList(batchNumber, userVo.getLong("id"), entry2.getValue(), tenantId));
                }
            }
        }
        return allocateRecordList;
    }

    private List<GzxAllocateRecord> getBatchAllocateList(String batchNumber, Long mediatorId, List<String> debtorCardId, String tenantId) {
        String createUserId = StpLoginUserUtil.getLoginUser().getId();
        List<GzxAllocateRecord> resultList = new ArrayList<>();
        long startTime = System.currentTimeMillis();
        List<List<String>> batchDebtorCardIdList = Lists.partition(debtorCardId, IntConstant.VALUE_1000);
        for (List<String> debtorCardIdList : batchDebtorCardIdList) {
            List<GzxAllocateRecord> allocateRecordList = allocateRecordDbService.selectLatestRecordsByBatchAndDebtorCards(batchNumber, debtorCardIdList);
            log.info("案件分配---查询耗时：{}", System.currentTimeMillis() - startTime);
            Set<String> needWithdrawDebtorCardIdSet = allocateRecordList.stream()
                    .filter(s -> Objects.equals(s.getType(), GzxAllocateType.ALLOCATE))
                    .map(GzxAllocateRecord::getDebtorCardId).collect(Collectors.toSet());
            List<GzxAllocateRecord> allocateResult = new ArrayList<>();
            List<GzxAllocateRecord> withdrawResult = new ArrayList<>();
            for (String str : debtorCardId) {
                GzxAllocateRecord allocateRecord = new GzxAllocateRecord();
                allocateRecord.setType(GzxAllocateType.ALLOCATE);
                allocateRecord.setBatchNumber(batchNumber);
                allocateRecord.setDebtorCardId(CommonCryptogramUtil.doSm1AesDecrypt(str));
                allocateRecord.setMediatorId(mediatorId);
                allocateRecord.setCreateUser(createUserId);
                allocateRecord.setTenantId(tenantId);
                allocateResult.add(allocateRecord);
                if (needWithdrawDebtorCardIdSet.contains(str)) {
                    GzxAllocateRecord withdrawAllocateRecord = new GzxAllocateRecord();
                    BeanUtils.copyProperties(allocateRecord, withdrawAllocateRecord);
                    withdrawAllocateRecord.setType(GzxAllocateType.WITHDRAW);
                    withdrawResult.add(withdrawAllocateRecord);
                }
            }
            resultList.addAll(withdrawResult);
            resultList.addAll(allocateResult);
        }
        return resultList;
    }

    private void saveAllocateToDb(Map<String, Map<String, List<String>>> caseMap, List<GzxAllocateRecord> allocateRecordList, SaBaseLoginUser loginUser) {
        Set<String> noAccountList = new HashSet<>();
        Set<String> errorAccountList = new HashSet<>();
        List<GzxAllocateCaseDto> tryList = new ArrayList<>();
        try {
            TenantContext.disableTenantFilter();
            long startTime = System.currentTimeMillis();
            //保存到分案记录表
            allocateRecordDbService.saveBatch(allocateRecordList);
            //修改主表分案
            Map<String, JSONObject> userMap = userCaffeine.getUserMapByTenantId(loginUser.getTenantId());
            for (Map.Entry<String, Map<String, List<String>>> entry : caseMap.entrySet()) {
                String batchNumber = entry.getKey();
                //根据调解员分组
                for (Map.Entry<String, List<String>> entry2 : entry.getValue().entrySet()) {
                    String mediatorName = entry2.getKey();
                    JSONObject userVo = userMap.get(mediatorName);
                    if (userVo != null) {
                        long batchTime = System.currentTimeMillis();
                        List<List<String>> batchList = Lists.partition(entry2.getValue(), IntConstant.VALUE_300);
                        for (List<String> batch : batchList) {
                            GzxAllocateCaseDto allocateCaseDto = null;
                            String userName = userVo.getStr("name");
                            try {
                                long caseStartTime = System.currentTimeMillis();
                                allocateCaseDto = allocateCase(userVo.getLong("orgId"), userVo.getLong("id"), userName, batchNumber, batch);
                                caseDbService.allocateCase(allocateCaseDto);
                                log.info("{}：案件分配---修改案件主表，共计：{}条，耗时：{}", userName, batch.size(), System.currentTimeMillis() - caseStartTime);
                                CommonSleepUtil.sleep(IntConstant.VALUE_300);
                            } catch (Exception e) {
                                tryList.add(allocateCaseDto);
                            }
                        }
                        log.info("{}：案件分配---，共计：{}条，耗时：{}", mediatorName, entry2.getValue().size(), System.currentTimeMillis() - batchTime);
                    } else {
                        noAccountList.add(mediatorName);
                    }
                }
            }
            //重试
            if (CollectionUtil.isNotEmpty(tryList)) {
                for (GzxAllocateCaseDto allocateCaseDto : tryList) {
                    try {
                        caseDbService.allocateCase(allocateCaseDto);
                    } catch (Exception e) {
                        log.error("{}分案出现异常", allocateCaseDto.getMediatorName(), e);
                        errorAccountList.add(allocateCaseDto.getMediatorName());
                    }
                }
            }
            CommonWechatUtil.sendMessage("案件分配【结束】", loginUser.getName() + "的分案任务处理完成，耗时：" + (System.currentTimeMillis() - startTime), WechatNotificationType.TASK_END, List.of(loginUser.getEmpNo()));
        } catch (Exception e) {
            log.error("分案出现异常：", e);
        } finally {
            if (CollectionUtil.isNotEmpty(noAccountList)) {
                CommonWechatUtil.sendMessage("案件分配【异常】", loginUser.getName() + "的分案任务，" + noAccountList + "---以上账号因在自研系统匹配不到，分案失败，其余调解员的案件正常分配并结束", WechatNotificationType.TASK_ERROR, List.of(loginUser.getEmpNo()));
            }
            if (CollectionUtil.isNotEmpty(errorAccountList)) {
                CommonWechatUtil.sendMessage("案件分配【异常】", loginUser.getName() + "的分案任务出现异常，" + errorAccountList + "---分案失败", WechatNotificationType.TASK_ERROR, List.of("ZhuJingTian-BeiJing", loginUser.getEmpNo()));
            }
            TenantContext.enableTenantFilter();
            TenantContext.clear();
        }
    }

    private GzxAllocateCaseDto allocateCase(Long orgId, Long userId, String userName, String batchNumber, List<String> debtorCardIdList) {
        GzxAllocateCaseDto allocateCaseDto = new GzxAllocateCaseDto();
        allocateCaseDto.setOrgId(orgId);
        allocateCaseDto.setMediatorId(userId);
        allocateCaseDto.setMediatorName(userName);
        allocateCaseDto.setBatchNumber(batchNumber);
        allocateCaseDto.setDebtorCardIdList(debtorCardIdList);
        return allocateCaseDto;
    }
}
